/* OTA example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include "ota.h"
#include "../aws_esp32/aws_iot_Publish.h"
#include "../aws_esp32/aws_iot_connect.h"

#define OTA_BufSize (1024*4)
static unsigned char ota_task_init_flag = 0;

static const char *TAG = "OTA客户端";
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
unsigned int content_length = 0;

static void task_ota_install();

static int downloadLength = 0; // 已下载字节数
static bool downloadSuccess = false;

esp_partition_t *update_partition = NULL;
esp_ota_handle_t update_handle = NULL;
uint8_t isOTATrigger = 0; //0xff;
SemaphoreHandle_t sync_ota_download;
unsigned int OTA_percentage; //OTA进度
esp_http_client_handle_t client = NULL;


static void http_cleanup(void)
{
    if(client != NULL)
    {
        esp_http_client_close(client);
        esp_http_client_cleanup(client);
        client = NULL;
        ESP_LOGI(TAG, "关闭并释放 http");
    }
}
esp_err_t _http_event_handle(esp_http_client_event_t *evt) //http时间处理
{
    switch (evt->event_id)
    {
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGI(TAG, "OTA Connected");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGI(TAG, "OTA Sent");
            break;
        case HTTP_EVENT_ON_HEADER:
            // ESP_LOGI(TAG, evt->header_key);
            // ESP_LOGI(TAG, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            esp_ota_write(update_handle, evt->data, evt->data_len);
            downloadLength += evt->data_len;
            if((downloadLength > 0) && (content_length > 0)) OTA_percentage = (downloadLength * 100 / content_length);
            AWS_IoT_PublishMsg.PubMsg_OTA = OTA_percentage;
            ESP_LOGI(TAG, "http event downloaded=%d, total = %d, percentage = %d ", downloadLength, content_length, OTA_percentage);
            ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
            break;
        case HTTP_EVENT_ON_FINISH:

            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "OTA Disconnected");
            break;
        case HTTP_EVENT_ERROR:
            ESP_LOGI(TAG, "OTA Error");
            break;
    }
    return ESP_OK;
}


void OTA_TaskDelete(void)
{
    http_cleanup();
    ESP_LOGI(TAG, "Task Delete");
    sync_ota_download = NULL;
    vTaskDelete(NULL);
}
void task_ota_download()
{
    ESP_LOGI(TAG, "Downloading newest version...");
    esp_err_t err = ESP_OK;

    //下载OTA文件
    esp_http_client_config_t config = {
        .url = "https://s3.us-east-2.amazonaws.com/xw.vertical/Ksirb_firmware.bin",
        .cert_pem = root_cert_auth_pem_start,
        .timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,
        .skip_cert_common_name_check = false,
        .event_handler = _http_event_handle,
        .buffer_size = OTA_BufSize,
        .keep_alive_enable = true,
    };

    update_partition = esp_ota_get_next_update_partition(NULL);        //进入下一个分区
    esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); //向指定分区写入OTA更新

    int tryTimes = 0;
    char rangeValue[100];

    while (1)
    {
        client = esp_http_client_init(&config); //创建客户端配置
        sprintf(rangeValue, "bytes=%d-", downloadLength);                //断电续传配置
        if(client != NULL)
        {
            esp_http_client_set_header(client, "Range", rangeValue);         //记录下载进程
            esp_http_client_perform(client); //执行http配置任务
        }
        http_cleanup();
        //下载溢出报错
        if (content_length != 0 && content_length < downloadLength)
        {
            ESP_LOGE(TAG, "Download failed because content length is incorrect");
            break;
        }
        //下载完成
        if ((downloadLength == content_length) && (downloadLength > 0))
        {
            ESP_LOGW(TAG, "Download successful.");
            downloadSuccess = true;
            vTaskDelay(2000 / portTICK_RATE_MS); //给AWS更新OTA进度100%,提供缓冲时间

            esp_ota_end(update_partition); //完成ota更改映射至新的分区
            ESP_LOGW(TAG, "Installing firmware update...");
            esp_ota_set_boot_partition(update_partition); //配置新的OTA文件 
            break;
        }

        tryTimes++;
        ESP_LOGI(TAG, "[WARNING] Firmware download broke down in range %d, try %d", downloadLength, tryTimes);
        vTaskDelay(500 / portTICK_RATE_MS);
    }
    if (!downloadSuccess) //报错退出
    {
        ESP_LOGE(TAG, "[ERROR] Firmware upgrade fails with try time %d", tryTimes);
        OTA_TaskDelete();
        return;
    }
    OTA_TaskDelete();
}

void task_ota_check()
{
    extern volatile unsigned int firmware_number; //固件版本号
    const esp_partition_t *update_partition = NULL;
    esp_err_t err = ESP_FAIL;
    char newest_version_char[100];
    char newest_version_c_to_i[10];
    char content_length_c_to_i[20];
    unsigned int newest_version_int = 0;

    ESP_LOGI(TAG, "等待 AWS订阅成功后执行 OTA任务");
    if(sync_ota_download == NULL) sync_ota_download = xSemaphoreCreateBinary();
    xSemaphoreTake(sync_ota_download, portMAX_DELAY);
    ESP_LOGI(TAG, "从AWS S3校验OTA版本");

    //读取索引文件信息
    // int data_read = esp_http_client_read(client, newest_version_char, 100);
    int data_read = -1;
    client = NULL;
    while (data_read <= 0)
    { //读取失败 放弃下载任务
        //下载索引文件
        esp_http_client_config_t config = {
            .url = "https://s3.us-east-2.amazonaws.com/xw.vertical/.Ksirb_version",
            .cert_pem = (char *)root_cert_auth_pem_start,
            .timeout_ms = 5000,
            .buffer_size = 400,
            .skip_cert_common_name_check =  false,
        };
        client = esp_http_client_init(&config);
        ESP_LOGI(TAG, "初始化.");
        if(client != NULL) err = esp_http_client_open(client, 200); //该函数有BUG：会持续消耗内存,将近消耗40k内存
        if(err == ESP_OK)
        {
            ESP_LOGI(TAG, "连接成功");
            if(client != NULL) esp_http_client_fetch_headers(client); //读取 HTTP Server 响应 headers. 否则无法读取
            ESP_LOGI(TAG, "读取 HTTP Server 响应 headers.");
            //初始化字符串
            for (int i = 0; i < 100; i++)
            {
                newest_version_char[i] = '\0';
                newest_version_c_to_i[i] = '\0';
                content_length_c_to_i[i] = '\0';
            }
            if(client != NULL) data_read = esp_http_client_read(client, newest_version_char, 100);
            ESP_LOGI(TAG, "读取OTA");
        }
        http_cleanup();

        vTaskDelay(500 / portTICK_RATE_MS);
    }
    //完成文件读取
    ESP_LOGI(TAG, "Download is %s", newest_version_char);
    //移位至第一个'#'
    for (int i = 0; i < 100; i++)
    {
        if (newest_version_char[0] != 0x23)
        {
            for (int j = 0; j < 100; j++)
            {
                newest_version_char[j] = newest_version_char[j + 1];
            }
        }
    }
    //去除第一个'#'
    for (int j = 0; j < 100; j++)
    {
        newest_version_char[j] = newest_version_char[j + 1];
    }
    //复制数字位
    for (int i = 0; i < 10; i++)
    {
        if (newest_version_char[i] != 0x20)
        {
            newest_version_c_to_i[i] = newest_version_char[i];
        }
        else
        {
            break;
        }
    }
    newest_version_int = atoi(newest_version_c_to_i);
    ESP_LOGI(TAG, "current version is %d, newset version int is %d,", firmware_number , newest_version_int);

    if (firmware_number < newest_version_int)
    {
        ESP_LOGI(TAG, "find new version prepare to download");
        //读取OTA bin文件长度
        for (int i = 0; i < 100; i++)
        {
            if (newest_version_char[0] != 0x20)
            {
                for (int j = 0; j < 100; j++)
                {
                    newest_version_char[j] = newest_version_char[j + 1];
                }
            }
        }
        //复制数字位
        for (int i = 0; i < 20; i++)
        {
            if (newest_version_char[i] != 0x23)
            {
                content_length_c_to_i[i] = newest_version_char[i];
            }
            else
            {
                break;
            }
        }
        content_length = atoi(content_length_c_to_i);
        ESP_LOGI(TAG, "content_length is %d", content_length);
        xTaskCreate(&task_ota_download, "task_ota_download", 1024 * 4, NULL, 1, NULL);
    }
    else
    {
        ESP_LOGI(TAG, "current version is newset, OTA download cancel");
    }
    OTA_TaskDelete();
}

esp_err_t OTA_Init()
{
    xTaskCreate(&task_ota_check, "task_ota_check", 1024 * 4, NULL, 1, NULL);
    return ESP_OK;
}

void OTA_Trigger(void)
{
    if(isOTATrigger == 0)
    {
        isOTATrigger = 1;

        if(sync_ota_download != NULL)
        {
            ESP_LOGI(TAG, "触发 OTA任务");
            xSemaphoreGive(sync_ota_download);
        }
    }
}
